﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using System.Net;

namespace XrmLinq.Entities
{
    /// <summary>
    /// Helper class that simplifies creating an instance of the CRM web service. It supports on-premise and partner hosted methods.
    /// </summary>
    /// <example>
    /// <para>
    /// On-Premise connection with credentials.
    /// </para>
    /// <code>
    /// <![CDATA[
    /// CrmService sdk = Connection.Create("http://host.com:port/mscrmservices/2007/crmservice.asmx", "OrgName", "Domain", "Administrator", "Password");
    /// ]]>
    /// </code>
    /// <para>
    /// On-Premise connection with network credentials.
    /// </para>
    /// <code>
    /// <![CDATA[
    /// CrmService sdk = Connection.Create("http://host.com:port/mscrmservices/2007/crmservice.asmx", "OrgName");
    /// ]]>
    /// </code>
    /// <para>
    /// Partner hosted connection.
    /// </para>
    /// <code>
    /// <![CDATA[
    /// CrmService sdk = Connection.Create("https://discovery.mscrm.host.com/mscrmservices/2007/spla/crmdiscoveryservice.asmx", "OrgName", "Domain", "Administrator", "Password");
    /// ]]>
    /// </code>
    /// </example>
    public class Connection
    {
        /// <summary>
        /// Create a connection to the CRM web service using Network Credentails.
        /// </summary>
        /// <param name="url">Url of the crm web service.</param>
        /// <param name="organization">Organization name.</param>
        /// <returns>An instance of <see cref="CrmService"/>.</returns>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// CrmService sdk = Connection.Create("http://host.com:port/mscrmservices/2007/crmservice.asmx", "OrgName");
        /// ]]>
        /// </code>
        /// </example>
        public static CrmService Create(string url, string organization)
        {
            return Create(url, organization, "", "", "");
        }

        /// <summary>
        /// Create a connection to the CRM web service using the specified credentails. This method supports connecting to partner-hosted instances.
        /// </summary>
        /// <param name="url">Url of the crm web service or IFD discovery url.</param>
        /// <param name="organization">Organization name.</param>
        /// <param name="domain">Domain name of the user.</param>
        /// <param name="userName">Username.</param>
        /// <param name="password">Password.</param>
        /// <returns>An instance of <see cref="CrmService"/>.</returns>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// CrmService sdk = Connection.Create("http://host.com:port/mscrmservices/2007/crmservice.asmx", "OrgName", "Domain", "Administrator", "Password");
        /// ]]>
        /// </code>
        /// <para>
        /// IFD connection
        /// </para>
        /// <code>
        /// <![CDATA[
        /// CrmService sdk = Connection.Create("https://discovery.mscrm.host.com/mscrmservices/2007/spla/crmdiscoveryservice.asmx", "OrgName", "Domain", "Administrator", "Password");
        /// ]]>
        /// </code>
        /// </example>
        public static CrmService Create(string url, string organization, string domain, string userName, string password)
        {
            if (url.IndexOf("CrmDiscoveryService.asmx", StringComparison.InvariantCultureIgnoreCase) > -1) // IFD
            {
                return AuthenticateViaIFD(url, organization, domain, userName, password);
            }
            return new CrmService
            {
                CrmAuthenticationTokenValue = new CrmAuthenticationToken
                {
                    AuthenticationType = 0,
                    OrganizationName = organization
                },
                Url = url,
                Credentials = (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) ?
                CredentialCache.DefaultNetworkCredentials : new NetworkCredential(userName, password, domain)
            };
        }

        private static CrmService AuthenticateViaIFD(string url, string organization, string domain, string userName, string password)
        {
            CrmService sdk = null;
            using (CrmDiscoveryService disco = new CrmDiscoveryService
            {
                Url = url
            })
            {
                RetrieveOrganizationsRequest request = new RetrieveOrganizationsRequest
                {
                    UserId = string.Format(@"{0}\{1}", domain, userName),
                    Password = password
                };
                RetrieveOrganizationsResponse response = (RetrieveOrganizationsResponse)disco.Execute(request);

                OrganizationDetail info = null;
                foreach (OrganizationDetail detail in response.OrganizationDetails)
                {
                    if (detail.OrganizationName.Equals(organization, StringComparison.InvariantCultureIgnoreCase))
                    {
                        info = detail;
                        break;
                    }
                }

                if (info != null)
                {
                    RetrieveCrmTicketRequest ticketRequest = new RetrieveCrmTicketRequest
                    {
                        OrganizationName = info.OrganizationName,
                        UserId = request.UserId,
                        Password = request.Password
                    };
                    RetrieveCrmTicketResponse ticketResponse = (RetrieveCrmTicketResponse)disco.Execute(ticketRequest);

                    CrmAuthenticationToken token = new CrmAuthenticationToken
                    {
                        AuthenticationType = 2,
                        OrganizationName = info.OrganizationName,
                        CrmTicket = ticketResponse.CrmTicket
                    };

                    sdk = new CrmService
                    {
                        CrmAuthenticationTokenValue = token,
                        Url = info.CrmServiceUrl
                    };
                }
            }
            return sdk;
        }
    }
}
